/*
 * TypingStructure.c
 * VB Script Analyzer
 * @author Gregoire JACOB (gregoire.jacob@orange-ftgroup.com)
 * @date 25/01/2008
 * @version 1.0
 */

#include ".\Includes\Analyzer.h"

/* Printing and storing the typing structure **************************/
void printTypedObject(struct TYPED_OBJ * object, int number){
	int i;

	printf("\\* Object %s%d: ",typestr[object->Type],number);
	printf("%s",object->ObjectName);
	printf("\nNature: %s\n",naturestr[object->Nature]);
	printf("Status: %d\n",object->Status);
	if(object->nbalias){
		printf("Aliases:\n");
		for(i=0;i<object->nbalias;i++)
			printf("     | -%s\n",object->ObjectAliases[i]);
	}//if aliases
	if(object->nbref){
		printf("References:\n");
		for(i=0;i<object->nbref;i++)
			printf("     | -%s\n",object->ObjectReferences[i]);
	}//if references
	printf("/* End Object\n");
}

void printfTypedObject(FILE * f, struct TYPED_OBJ * object, int number){
	int i;

	fprintf(f,"\\* Object %s%d: ",typestr[object->Type],number);
	fprintf(f,"%s",object->ObjectName);
	fprintf(f,"\nNature: %s\n",naturestr[object->Nature]);
	fprintf(f,"Status: %d\n",object->Status);
	if(object->nbalias){
		fprintf(f,"Aliases:\n");
		for(i=0;i<object->nbalias;i++)
			fprintf(f,"     | -%s\n",object->ObjectAliases[i]);
	}//if aliases
	if(object->nbref){
		fprintf(f,"References:\n");
		for(i=0;i<object->nbref;i++)
			fprintf(f,"     | -%s\n",object->ObjectReferences[i]);
	}//if references
	fprintf(f,"/* End Object\n");
}

void printTyping(struct TYPING * types){
	int i;

	printf("\\** Typing: *********************************************/\n");
	printf("Typed Object List:\n");
	for(i=0;i<types->nbobj;i++){
		printf("------------------------\n");
		printTypedObject(&types->objects[i],i);
	}
	printf("------------------------\n");
	printf("\n");
	printf("\\** End Typing: ****************************************/\n\n\n");
}

void printfTyping(FILE * f, struct TYPING * types){
	int i;

	fprintf(f,"\n\n\\** Typing: *********************************************/\n");
	fprintf(f,"Typed Object List:\n");
	for(i=0;i<types->nbobj;i++){
		fprintf(f,"------------------------\n");
		printfTypedObject(f,&types->objects[i],i);
	}
	fprintf(f,"------------------------\n");
	fprintf(f,"\n");
	fprintf(f,"\\** End Typing: ****************************************/\n\n\n");
}


void storeTypedObject(FILE * db, struct TYPED_OBJ * object){
	int idata; char cdata = 0; int i;
	//Storing characteristics
	if(object->ObjectName[0]!=0){ //Name presence
		cdata = 'n';
		fwrite(&cdata,sizeof(char),1,db);
		//Remove end of line inside the name (for loadin with fgets)
		for(i=0;i<NAME_MAX_LENGTH;i++){
			if(object->ObjectName[i]=='\n') object->ObjectName[i]='#';
			if(object->ObjectName[i]=='\r') object->ObjectName[i]='#';
			if(object->ObjectName[i]== 0) break;
		}
		//Store name
		if(object->ObjectName[0]=='"'){
			fprintf(db,"%s",object->ObjectName);
		}else{
			fprintf(db,"\"%s\"",object->ObjectName);
		}
		cdata = '\n';
		fwrite(&cdata,sizeof(char),1,db); //Null-terminated string
	}else{
		cdata = 'u';
		fwrite(&cdata,sizeof(char),1,db);
	}
	idata = object->Type;
	fwrite(&idata,sizeof(int),1,db);
	idata = object->Nature;
	fwrite(&idata,sizeof(int),1,db);
	idata = object->Status;
	fwrite(&idata,sizeof(int),1,db);
}


void storeTyping(FILE * db, struct TYPING * types){
	int i,nb = types->nbobj;
	fwrite(&nb,sizeof(int),1,db);
	for(i=0;i<nb;i++){
		storeTypedObject(db,&types->objects[i]);
	}
}
/**********************************************************************/

/* Accessing fields of the typing structure ***************************/
int isKnownObject(struct TYPING * types, char *  token){
	int i, j, len, q = 0;
	char unquoted[TOKENSIZE];
	if(token[0]=='"'){
		len = (int)strlen(token);
		if(len>2&&len<TOKENSIZE){
			strcpy_s(unquoted,TOKENSIZE,&token[1]);
			unquoted[len-2] = 0;
			q=1;
		}
	}

	for(i=0;i<types->nbobj;i++){
		if(!strcasecmp(token,types->objects[i].ObjectName)) return i;
		if(q&&!strcasecmp(unquoted,types->objects[i].ObjectName)) return i;
		for(j=0;j<types->objects[i].nbalias;j++){
			if(!strcasecmp(token,types->objects[i].ObjectAliases[j])) return i;}
		for(j=0;j<types->objects[i].nbref;j++){
			if(!strcasecmp(token,types->objects[i].ObjectReferences[j])) return i;}
	}
	return -1;
}

int getObjectType(struct TYPING * types, int obj){
	return types->objects[obj].Type;
}

int getObjectNature(struct TYPING * types, int obj){
	return types->objects[obj].Nature;
}
/**********************************************************************/

/* Updating fields of the typing structure ****************************/
void setObjectType(struct TYPING * types, int obj, int type){
	types->objects[obj].Type = type;
}

void setObjectNature(struct TYPING * types, int obj, int nature){
		types->objects[obj].Nature = nature;
}

void setObjectStatus(struct TYPING * types, int obj, int status){
	types->objects[obj].Status = status;
}

int addNewObject(struct TYPING * types, char *  name, int nature){
	int index = types->nbobj;
	types->nbobj++;
	//Name
	if(name)
		strcpy_s(types->objects[index].ObjectName,NAME_MAX_LENGTH,name);
	types->objects[index].Nature = nature;
	updateObjectType(types,index);
	return index;
}

void addObjectReference(struct TYPING * types, int obj, char * reference){
	int objk;
	int nb = types->objects[obj].nbref;
	//Checks for existing reference
	objk = isKnownObject(types,reference);
	if(objk>-1){
		if(objk==obj) return;
		removeObjectReference(types,objk,reference);
	}
	types->objects[obj].nbref++;
	types->objects[obj].ObjectReferences[nb] = (char *)calloc(NAME_MAX_LENGTH,sizeof(char));
	strcpy_s(types->objects[obj].ObjectReferences[nb],NAME_MAX_LENGTH,reference);
	updateObjectType(types,obj);
}

void addObjectAlias(struct TYPING * types, int obj, char * alias){
	int i;
	int nb = types->objects[obj].nbalias;
	//Checks for existing alias
	for(i=0; i<nb; i++){
		if(!strcasecmp(alias,
			types->objects[obj].ObjectAliases[i])){
				return;
		}
	}
	types->objects[obj].nbalias++;
	types->objects[obj].ObjectAliases[nb] = (char *)calloc(NAME_MAX_LENGTH,sizeof(char));
	strcpy_s(types->objects[obj].ObjectAliases[nb],NAME_MAX_LENGTH,alias);
	updateObjectType(types,obj);
}

void removeObjectReference(struct TYPING * types, int obj, char * reference){
	int i, j, nb = types->objects[obj].nbref;
	//Looks for the reference index
	for(i=0; i<nb; i++){
		if(!strcasecmp(reference,
			types->objects[obj].ObjectReferences[i])){
			for(j=i; j<nb-1; j++){
				strcpy_s(types->objects[obj].ObjectReferences[j],
					NAME_MAX_LENGTH, types->objects[obj].ObjectReferences[j+1]);
			}
			types->objects[obj].nbref--;
			break;
		}//reference found
	}
}

void updateObjectType(struct TYPING * types, int obj){
	int highesttype = types->objects[obj].Type;
	int nature = types->objects[obj].Nature;
	int localnature = nature;
	int highestnature = nature;
	int i,type;
	
	if(nature==VAR){
		types->objects[obj].Type = TYPE_VAR;
		return;
	}

	type = ClassifyObject(types->objects[obj].ObjectName,&localnature);
	if(type > highesttype){
		highesttype = type;
		highestnature = localnature;
	}
	for(i=0;i<types->objects[obj].nbref;i++){
		localnature = nature;
		type = ClassifyObject(types->objects[obj].ObjectReferences[i],&localnature);
		if(type > highesttype){
			highesttype = type;
			highestnature = localnature;
		}
	}
	for(i=0;i<types->objects[obj].nbalias;i++){
		localnature = nature;
		type = ClassifyObject(types->objects[obj].ObjectAliases[i],&localnature);
		if(type > highesttype){
			highesttype = type;
			highestnature = localnature;
		}
	}
	types->objects[obj].Type = highesttype;
	types->objects[obj].Nature = highestnature;
}

int updateTypesAfterSysCall(struct TYPING * types, char * param, 
							char * ret, int nature, int status){
	int obj = -1, objret = -1, named = 0, exnature;

	if(param[0]=='"'){
		named = 1;		//Object referenced through its name (otherwise alias)
		unquote(param);
	}
	obj = isKnownObject(types,param);
	if(obj==-1){//unknown object
		if(named){
			obj = addNewObject(types,param,nature);
		}else{
			obj = addNewObject(types,NULL,nature);
			addObjectAlias(types,obj,param);
		}
		setObjectStatus(types,obj,status);
	}else{//known object
		exnature = getObjectNature(types,obj);
		if(exnature == 0){
			types->objects[obj].Nature = nature;
			//classification may be affined by the nature
			updateObjectType(types,obj);
		}else if(nature&&exnature!=nature){
			printf("[-] Trying to update objects inconsistent by nature\n");
			if(exnature==VAR){ 
				types->objects[obj].Nature = nature;updateObjectType(types,obj);
			}
			if(nature==VAR){
				if(named){
					obj = addNewObject(types,param,nature);
				}else{
					obj = addNewObject(types,NULL,nature);
					addObjectAlias(types,obj,param);
				}
			}
		}
	}
	if(ret){
		objret = isKnownObject(types,ret);
		//If reference was already affected
		if(objret>-1) removeObjectReference(types,objret,ret);
		addObjectReference(types,obj,ret);
	}
	return obj;
}


int updateTypesBeforeLocCall(struct TYPING * types, char * param, char * arg){
	int obj = -1, named = 0;

	if(param[0]=='"'){
		named = 1;		//Object referenced through its name (otherwise alias)
		unquote(param);
	}
	obj = isKnownObject(types,param);
	if(obj==-1){ //unknown object
		if(named){
			obj = addNewObject(types,param,0);
		}else{
			obj = addNewObject(types,NULL,0);
			addObjectAlias(types,obj,param);
		}
	}
	addObjectReference(types,obj,arg);
	return obj;
}

int updateReturnStorage(struct TYPING * types, char * function, char * value){
	int retobj = -1, storobj = -1, named = 0;

	if(value[0]=='"'){
		named = 1;		//Name affected to the return value (otherwise alias)
		unquote(value);
	}
	retobj = isKnownObject(types,function);
	if(strlen(value)>0){//Ignore empty strings
		storobj = isKnownObject(types,value);
	}
	if(retobj==-1){ //Unknown return object
		if(storobj>-1){//If the returned object is known
			retobj = storobj;
			addObjectReference(types,storobj,function);
		}else{ //Unknown returned object
			retobj = addNewObject(types,NULL,0);
			addObjectReference(types,retobj,function);
			if(named){
				strcpy_s(types->objects[retobj].ObjectName,NAME_MAX_LENGTH,value);
			}else{
				addObjectAlias(types,retobj,value);
			}
		}
	}else{ //Return value is already stored 
		//return object is only updated when significant (typing order)
		if(storobj>-1 && //If the returned object is known
			types->objects[storobj].Type>types->objects[retobj].Type){
				removeObjectReference(types,retobj,function);
				addObjectReference(types,storobj,function);
				retobj = storobj;
		}else{
			addObjectAlias(types,retobj,value);
		}
	}
	return retobj;
}
/**********************************************************************/
